package thingapi

import (
	"encoding/json"
	"errors"
	"fmt"
	"iot-base/common/config"
	"iot-base/common/dbconn"
	"iot-base/common/dbobj"
	"iot-base/common/webapi"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
)

func parseRule(rule string) []interface{} {
	var maprule map[string]interface{}
	err := json.Unmarshal([]byte(rule), &maprule)
	if err != nil {
		return nil
	}
	containRule := maprule["contain_rule"]
	if containRule != nil {
		rules, ok := containRule.([]interface{})
		if ok {
			return rules
		}

	}
	fmt.Println("error change to maprule", containRule)
	return nil
}

const edgeModelName = "IOTBASEmEdgeCTL"

func createStationChildren(id string) bool {
	var rule string
	//var name string
	row := dbconn.DBConnection.Raw(`select t_models.containner_rule  from t_models,things where
	things.model_id = t_models.uid and things.uid=?`, id).Row()
	//defer row.Close()
	if row == nil {
		fmt.Println("row is nil")
		return false
	}
	fmt.Println("row ", row)
	row.Scan(&rule)
	isEdgeCreated := false
	things := make([]*dbobj.Thing, 0, 5)
	stations := make([]*dbobj.ThingStation, 0, 5)
	if rule != "" {
		rules := parseRule(rule)
		if rules == nil {
			fmt.Println("rules is nil")
			return false
		}

		for _, rule := range rules {
			if rule != nil {
				mrule, ok := rule.(map[string]interface{})
				if !ok {
					fmt.Printf("to map error %T", rule)
					return false
				}
				modelID := mrule["model_id"]
				iCount := mrule["count"]
				prefix := mrule["prefix"]

				count, ok := iCount.(int)
				fmt.Printf("icount type %T\n", iCount)
				if ok == false {
					scount, ok := iCount.(string)
					if ok {
						count, _ = strconv.Atoi(scount)
					} else {
						fcount, ok := iCount.(float64)
						if ok {
							count = int(fcount)
						}
					}
				}
				var nameprefex = "子设备"
				model := dbobj.TModel{}
				em := dbconn.DBConnection.Model(&model).Where("uid=?", modelID).First(&model).Error
				if em != nil {
					continue
				}
				if prefix == nil {
					if model.UID != "" {
						nameprefex = model.SName
					} else {
						nameprefex = modelID.(string)
					}

				} else {
					nameprefex = prefix.(string)
				}
				fmt.Println("get count-------", count, iCount)

				for i := 0; i < count; i++ {
					sname := fmt.Sprintf("%s#%d", nameprefex, i+1)
					thing := dbobj.Thing{
						ModelID: modelID.(string),
						//DBObjIDName: dbconn.DBObjIDName{SName: sname},
						SName: sname,
						//SName : DBObjIDName(name,
					}
					thingstation := dbobj.ThingStation{
						ThingID:   thing.UID,
						StationID: id,
						ModelID:   modelID.(string),
						SNO:       i, //从0开始
					}
					if model.IsEdgeControl || (config.PType == "edge" && thing.ModelID == edgeModelName) {
						thing.UID = config.SelfOption.ID
						isEdgeCreated = true
						//dbconn.DBConnection.Model(&dbobj.Thing{}).Save(&thing)
						things = append([]*dbobj.Thing{&thing}, things...)
						stations = append([]*dbobj.ThingStation{&thingstation}, stations...)
					} else {
						//dbconn.NewItem(&thing)
						things = append(things, &thing)
						stations = append(stations, &thingstation)
					}
				}

			}
		}

	} else {
		fmt.Println("db------------row error", rule, row)
		return false
	}
	if config.PType == "edge" && !isEdgeCreated {
		thing := dbobj.Thing{
			ModelID: edgeModelName,
			//DBObjIDName: dbconn.DBObjIDName{SName: sname},
			SName: "Self",
			UID:   config.SelfOption.ID,
			//SName : DBObjIDName(name,
		}

		thingstation := dbobj.ThingStation{
			ThingID:   thing.UID,
			ModelID:   thing.ModelID,
			StationID: id,
			SNO:       1, //从0开始
		}
		things = append([]*dbobj.Thing{&thing}, things...)
		stations = append([]*dbobj.ThingStation{&thingstation}, stations...)

	}
	if len(things) > 0 {
		edge := things[0]
		for i, thing := range things {
			ts := stations[i]
			if edge.ModelID == edgeModelName {
				if i == 0 {
					thing.IsProxy = true
					thing.ProxyThingID = ""
				} else {
					thing.IsProxy = false
					thing.ProxyThingID = edge.UID
				}
			}

			dbconn.DBConnection.Create(thing)
			ts.ThingID = thing.UID
			dbconn.DBConnection.Create(ts)
		}
	}

	return true

}

func setProxyID(thing *dbobj.Thing) {
	if config.PType == "edge" { //如果是边缘则，则设置该站的接入为边缘id
		thing.ProxyThingID = config.SelfOption.ID
	}
}

//CreateStation 创建station
func CreateStation(pStation *dbobj.Station, modelID string, level int64) error {
	var model dbobj.TModel
	model.UID = modelID
	err := dbconn.DBConnection.Find(&model).Error
	if err != nil {
		return err
	}
	var thing dbobj.Thing
	thing.ModelID = modelID
	thing.Level = level
	thing.IsStation = true
	thing.SName = pStation.SName

	setProxyID(&thing)
	err = dbconn.NewItem(&thing)
	if err != nil {
		return err
	}
	pStation.UID = thing.UID
	err = dbconn.NewItem(pStation)
	if err != nil {
		return err
	}
	succ := createStationChildren(thing.UID)
	if succ {
		return nil
	}
	return errors.New("create station children error")

}
func updateStation(c *gin.Context, method string) bool {
	var station dbobj.Station
	if err := c.ShouldBindBodyWith(&station, binding.JSON); err != nil {
		webapi.SendError("station "+err.Error(), webapi.ParamMissed, c)
		return true
	}
	fmt.Println("station--------------", station, method)
	var thing dbobj.Thing
	if err := c.ShouldBindBodyWith(&thing, binding.JSON); err != nil {
		webapi.SendError("thing "+err.Error(), webapi.ParamMissed, c)
		return true
	}
	fmt.Println("thing---------------------", thing, method)
	var err error
	thing.IsStation = true
	setProxyID(&thing)
	if method == "POST" {
		err = dbconn.NewItem(&thing)
	} else {
		err = dbconn.Update(&thing)
	}

	if err != nil {
		webapi.SendError("thing "+err.Error(), webapi.DBError, c)
		return true
	}
	station.UID = thing.UID
	if method == "POST" {
		err = dbconn.NewItem(&station)
	} else {
		err = dbconn.Update(&station)
	}

	if err != nil {
		webapi.SendError("thing "+err.Error(), webapi.DBError, c)
		return true
	}
	isChildren := c.Query("createChildren")
	if isChildren == "true" {
		createStationChildren(thing.UID)
	}
	webapi.SendResult(station, nil, c)
	return true

}

const sqlbase = `select stations.s_name,stations.uid,stations.area_id,stations.lon,stations.lat,
stations.level,stations.parent_id,
things.model_id,areas.s_name as area_name,areas.whole_name as area_whole_name,
areas.path as area_path,things.status_used,things.proxy_thing_id
from stations
left join areas
on areas.uid = stations.area_id
inner join things
on stations.uid = things.uid `

func getStationInfo(c *gin.Context) bool {
	id := c.Param("id")
	fmt.Println("==========---------", id)
	var result []map[string]interface{}
	if id != "condition" {
		children := c.Query("children")
		fmt.Println("***********", children)
		if children == "true" {
			things, err := getStationChildren(id)
			if err != nil {
				webapi.SendError(err.Error(), webapi.DBError, c)
			} else {
				webapi.SendResult(things, nil, c)
			}
			return true

		}
		sql := ""
		if id == "" {
			sql = sqlbase
		} else {
			sql = fmt.Sprintf("%s and stations.uid='%s'", sqlbase, id)
		}
		err := dbconn.DBConnection.Raw(sql).Find(&result).Error
		if err != nil {
			webapi.SendError(err.Error(), webapi.DBError, c)
		} else {
			webapi.SendResult(result, nil, c)
		}
		return true
	}
	conditions := c.Request.URL.Query()
	if conditions == nil {
		webapi.SendError("no condition", webapi.ParamMissed, c)
	} else {
		var sql string
		//cmap := make(map[string]interface{})
		for k, v := range conditions {
			if len(v) >= 0 && !strings.Contains(k, " ") {
				//cmap[k] = v[0]
				if k != "stations.level" && k != "things.status_used" {
					if v[0] != "" {
						sql += (" and " + k + " like '%" + v[0] + "%'")
					}

				} else {
					sql += (" and " + k + "=" + v[0])
				}
			}
		}
		fmt.Println("conditon---------", sql)
		err := dbconn.DBConnection.Raw(sqlbase + sql).Find(&result).Error
		if err != nil {
			webapi.SendError(err.Error(), webapi.DBError, c)
		} else {
			webapi.SendResult(result, nil, c)
		}
	}
	return true

}

func getStationChildren(id string) ([]map[string]interface{}, error) {
	var things []map[string]interface{}
	//err := dbconn.DBConnection.Model(&dbobj.ThingStation{}).Where("station_id=?",id).Association("Thing").Find(&things)
	err := dbconn.DBConnection.Model(&dbobj.Thing{}).Select(`things.*,thing_stations.sno,
	thing_states.state,t_models.s_name as model_name,t_models.catalog_id,
	thing_maintain_infos.used_time,thing_maintain_infos.buy_time,thing_maintain_infos.create_time,
	thing_maintain_infos.first_online_time as first_online_time,
	thing_states.state,thing_params.param_value as ip`).
		Joins("JOIN thing_stations ON thing_stations.thing_id = things.uid").
		Joins("left JOIN thing_states ON thing_states.thing_id = things.uid").
		Joins("JOIN t_models ON t_models.uid=things.model_id").
		Joins(`left JOIN thing_params ON thing_params.thing_id=things.uid 
				and (thing_params.param_id=? or thing_params.param_id=?)`, "IP", "ip").
		Joins("left JOIN thing_maintain_infos ON thing_maintain_infos.thing_id = things.uid").
		Where("thing_stations.station_id = ?", id).
		Order("things.uid").
		Find(&things).Error

	return things, err
}

func beforeRemoveStation(id string) error {
	var thing dbobj.Thing
	thing.UID = id
	fmt.Println("delete thing-------------", id)
	var count int64
	dbconn.DBConnection.Model(&dbobj.ThingStation{}).Where("station_id=?", id).Count(&count)
	if count == 0 {
		dbconn.DBConnection.Model(&dbobj.Station{}).Where("parent_id=?", id).Count(&count)
		if count == 0 {
			return dbconn.DBConnection.Unscoped().Delete(&thing).Error
		}
		return errors.New("场站还有子场站没有删除")

	} else {
		return errors.New("场站还有子设备没有删除")
	}

}
